package com.lagou.edu.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.annotation.*;
import com.lagou.edu.utils.ClazzUtil;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @author 应癫
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    private static Map<String, Object> map = new HashMap<>();  // 存储对象


    static {
        // 通过反射技术实例化对象并且存储待用（map集合）
        // 加载xml
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        // 解析xml
        SAXReader saxReader = new SAXReader();
        Document document = null;
        try {
            document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> componentScans = rootElement.selectNodes("//component-scan");
            Set<Class<?>> scanClazz = new HashSet<>();
            for (Element e : componentScans) {
                String packageName = e.attributeValue("base-package");
                Set<Class<?>> classes = ClazzUtil.getClasses(packageName);
                scanClazz.addAll(classes);
            }
            // 解析所有的类
            for (Class<?> c : scanClazz) {
                Annotation[] annotations = c.getAnnotations();
                for (Annotation a : annotations) {
                    if (a instanceof Component || a instanceof Repository || a instanceof Service) {
                        map.put(getTypeName(c), c.getDeclaredConstructor().newInstance());
                    }
                }
            }

            // 填充属性
            fillProps();
            // 解析 Transactional 注解
            fillExtName();
            parseTransactional();


        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static Object getBean(String id) {
        return map.get(id);
    }


    private static void fillProps() {
        Set<Map.Entry<String, Object>> entries = map.entrySet();
        entries.forEach((e) -> {
            Class<?> c = e.getValue().getClass();
            Field[] declaredFields = c.getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                if (field.isAnnotationPresent(Autowired.class)) {
                    try {
                        Object o2 = map.get(getTypeName(field.getType()));
                        field.set(e.getValue(), o2);
                    } catch (IllegalAccessException ex) {
                        ex.printStackTrace();
                    }

                }
            }
        });
    }

    private static void parseTransactional() {
        Map<Object, Object> tmpMap = new HashMap<>();
        map.entrySet().forEach((e) -> {
            Class<?> c = e.getValue().getClass();
            Annotation[] annotations = c.getAnnotations();
            Object o = e.getValue();
            // 避免代理类生成两次
            for (Annotation a : annotations) {
                if (a instanceof Transactional) {
                    ProxyFactory factory = (ProxyFactory) map.get("com.lagou.edu.factory.ProxyFactory");
                    if (c.getInterfaces() != null && tmpMap.get(o) == null) {
                        tmpMap.put(o, factory.getJdkProxy(o));
                    } else if (tmpMap.get(o) == null) {
                        tmpMap.put(o, factory.getCglibProxy(o));
                    }
                    map.put(e.getKey(), tmpMap.get(o));
                }
            }
        });
    }

    private static void fillExtName() {
        Map<String, Object> tmpMap = new HashMap<>();
        tmpMap.putAll(map);
        tmpMap.forEach((key, value) -> {
            Class<?> c = value.getClass();
            Annotation[] annotations = c.getAnnotations();
            for (Annotation a : annotations) {
                String name = c.getSimpleName().substring(0, 1).toLowerCase() + c.getSimpleName().substring(1);
                if (a instanceof Component) {
                    String name2 = ((Component) a).value();
                    if (!StringUtils.isEmpty(name2)) {
                        name = name2;
                    }
                    map.put(name, value);
                }
                if (a instanceof Service) {
                    String name2 = ((Service) a).value();
                    if (!StringUtils.isEmpty(name2)) {
                        name = name2;
                    }
                    map.put(name, value);

                }
                if (a instanceof Repository) {
                    String name2 = ((Repository) a).value();
                    if (!StringUtils.isEmpty(name2)) {
                        name = name2;
                    }
                    map.put(name, value);

                }

            }
        });
    }

    private static String getTypeName(Class<?> c) {
        return c.getInterfaces().length == 0 ? c.getName() : c.getInterfaces()[0].getName();
    }

}
